#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <iostream>
#include <string>
#include <cstring>
#include <sstream>
#include <list>
#include <pthread.h>
using namespace std;

void *getuserinput(void *threadid);
void *sendrequest(void *threadid);


pthread_mutex_t lock;
pthread_mutex_t mymutex;
pthread_cond_t count_cv;
list<char *> sentencelist;
char *serverip;
char *port;

/*main method*/
int main(){
	
    /*get the environment*/
    serverip = getenv("SERVER_ADDRESS");
    port = getenv("SERVER_PORT");
    
    /*initial the locks*/
    pthread_mutex_init(&lock, NULL);
    pthread_mutex_init(&mymutex,NULL);
    pthread_cond_init(&count_cv,NULL);
    
    /*create two threads*/
    pthread_t threads[2];
    
    /*send request to server*/
    pthread_create(&threads[1], NULL, sendrequest, (void *)1);
    
    /*get user input from stdin*/
    pthread_create(&threads[0], NULL, getuserinput,(void *)0);
    
    /*join two threads*/
    pthread_join(threads[0],NULL);
    pthread_join(threads[1],NULL);
                  
    /*destroy the locks*/
    pthread_mutex_destroy(&lock);
    pthread_mutex_destroy(&mymutex);
    pthread_exit(NULL);
    return 0;
}

/* This method keep getting user input from stdin and store the string *
 * into a list for the other thread to use.                            */

void *getuserinput(void *threadid) {
    
    /*a loop never ended*/
    while(true) {
        
        /*get user input*/
        char *mybuffer = new char[1024];
        
        fgets(mybuffer, 1024, stdin);
        
        int size = strlen(mybuffer);
        
        mybuffer[size-1]='\0';
        
        /*aquire the lock*/
        pthread_mutex_lock(&lock);
        
        /*store the string into a list*/
        sentencelist.push_back(mybuffer);
                
        /*release the lock*/
        pthread_mutex_unlock(&lock);
        
        /*signal the other thread if it is blocked since the list is empty*/
        pthread_cond_signal(&count_cv);
    }  
    pthread_exit(NULL);
}

/* This method create the socket and connect to the server to abtain service*/
void *sendrequest(void * threadid) {
        
    struct sockaddr_in server;
    
    pthread_mutex_lock(&mymutex);

    /*create the socket*/
    int sid = socket(AF_INET,SOCK_STREAM,0);
    if(sid == -1) {
        cerr << "fail to create socket" << endl;
        pthread_exit(NULL);
    }

    /*set up the socket*/
    struct hostent *host = gethostbyname(serverip);
    struct in_addr **hostip = (struct in_addr**)host->h_addr_list;
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(inet_ntoa(**hostip));
    server.sin_port = htons(atoi(port));

    /*connect to the server*/
    if(connect(sid,(struct sockaddr *)&server,sizeof(server))== -1) {
        cerr << "fail to connect to the server" << endl;
        pthread_exit(NULL);
    }
        
    /*send msg to server to abtain service*/
    while(true) {
        char *buffer;
        int buffer_len=0;
			
        /* if the list of strings is empty, the thread just blocked *
         * until getting user input                                 */
    
        if(sentencelist.empty()) {
            pthread_cond_wait(&count_cv,&mymutex);
        }
        
        /*aquire the lock*/
        pthread_mutex_lock(&lock);
        
        /*get the string from the list*/
        buffer=sentencelist.front();
        sentencelist.pop_front();

        /*release the lock*/
        pthread_mutex_unlock(&lock);
				
        /*create the buffer for sending*/
        
        buffer_len= strlen(buffer);
            
        char arr[1024];
            
        int i;
        for(i=4; i< buffer_len+4; i++){
            arr[i] = buffer[i-4];
        }
        arr[i] = '\0';
       
        /*store the size of the string to a 4 byte array*/ 
        arr[0]=(buffer_len >> 24) & 0xff;
        arr[1]=(buffer_len >> 16) & 0xff;
        arr[2]=(buffer_len >> 8) & 0xff;
        arr[3]=buffer_len & 0xff;
        
        /*send the string to the server*/
        if(send(sid, arr,buffer_len+4,0) == -1) {
            cerr << "fail to send msg to server" << endl;
            break;
        }

        memset(arr, 0, buffer_len+5);
        
        /*receive the string from the server*/
        if(recv(sid, arr,buffer_len+4,0) == -1) {
            cerr << "fail to receive msg from server" << endl;
            break;
        }

        /*print the converted string from server*/
        cout << "Server: ";
        for(int j=4;j<buffer_len+4;j++){
            cout << arr[j];
        }
        cout << endl;
        
        //deallocate the string
        delete buffer;
        
        //sleep for 2 seconds
        sleep(2);
        
        pthread_mutex_unlock(&mymutex);
			
		}
    
    close(sid);
    pthread_exit(NULL);
}
 
	    

